#include <fhe/core/INodeDesc.h>
#include <fhe/core/NodeFactory.h>
#include <fhe/core/Node.h>

namespace fhe
{
  namespace core
  {
    INodeDesc::INodeDesc()
    {
    }
    
    INodeDesc::~INodeDesc()
    {
    }
    
    void INodeDesc::setParent( const std::string& module, const std::string& name )
    {
      parentModule_ = module;
      parentName_ = name;
    }
    
    std::string INodeDesc::module() const
    {
      return module_;
    }
    
    void INodeDesc::setModule( const std::string& module )
    {
      module_ = module;
    }
    
    std::string INodeDesc::name() const
    {
      return name_;
    }
    
    void INodeDesc::setName( const std::string& name )
    {
      name_ = name;
    }
    
    void INodeDesc::addFunc( const IFuncDesc::Ptr& func )
    {
      FHE_ASSERT_MSG( !hasFunc( func->name() ), "duplicate func %s", func->name().c_str() );
      funcs_[ func->name() ] = func;
    }
    
    bool INodeDesc::hasFunc( const std::string& name ) const
    {
      return funcs_.find( name ) != funcs_.end();
    }
    
    IFuncDesc::Ptr INodeDesc::getFunc( const std::string& name ) const
    {
      FuncIterator func = funcs_.find( name );
      FHE_ASSERT_MSG( func != funcs_.end(), "unknown func %s", name.c_str() );
      return func->second;
    }
    
    INodeDesc::FuncIterator INodeDesc::funcsBegin() const
    {
      return funcs_.begin();
    }
    
    INodeDesc::FuncIterator INodeDesc::funcsEnd() const
    {
      return funcs_.end();
    }
    
    void INodeDesc::addVar( const IVarDesc::Ptr& var )
    {
      FHE_ASSERT_MSG( !hasVar( var->name() ), "duplicate var %s", var->name().c_str() );
      vars_[ var->name() ] = var;
    }
    
    bool INodeDesc::hasVar( const std::string& name ) const
    {
      return vars_.find( name ) != vars_.end();
    }
    
    IVarDesc::Ptr INodeDesc::getVar( const std::string& name ) const
    {
      VarIterator var = vars_.find( name );
      FHE_ASSERT_MSG( var != vars_.end(), "unknown var %s", name.c_str() );
      return var->second;
    }
    
    INodeDesc::VarIterator INodeDesc::varsBegin() const
    {
      return vars_.begin();
    }
    
    INodeDesc::VarIterator INodeDesc::varsEnd() const
    {
      return vars_.end();
    }
    
    void INodeDesc::addNodeInterface( const std::string& module, const std::string& name )
    {
      FHE_ASSERT_MSG( !hasNodeInterface( name ), "duplicate nodeInterface %s", name.c_str() );
      nodeInterfaces_[ name ] = std::make_pair( module, name );
    }
    
    bool INodeDesc::hasNodeInterface( const std::string& name ) const
    {
      return nodeInterfaces_.find( name ) != nodeInterfaces_.end();
    }
    
    std::pair< std::string, std::string > INodeDesc::getNodeInterface( const std::string& name ) const
    {
      NodeInterfaceIterator nodeInterface = nodeInterfaces_.find( name );
      FHE_ASSERT_MSG( nodeInterface != nodeInterfaces_.end(), "unknown nodeInterface %s", name.c_str() );
      return nodeInterface->second;
    }
    
    INodeDesc::NodeInterfaceIterator INodeDesc::nodeInterfacesBegin() const
    {
      return nodeInterfaces_.begin();
    }
    
    INodeDesc::NodeInterfaceIterator INodeDesc::nodeInterfacesEnd() const
    {
      return nodeInterfaces_.end();
    }
    
    void INodeDesc::build( Node* node ) const
    {
      if ( node->module().empty() )
      {
        node->setModule( module_ );
      }
      if ( node->type().empty() )
      {
        node->setType( name_ );
      }
      if ( !parentModule_.empty() && !parentName_.empty() )
      {
        FHE_ASSERT_MSG( NodeFactory::instance().hasNodeDesc( parentModule_, parentName_ ),
                        "unable to find parent %s.%s for nodeDesc %s.%s",
                        parentModule_.c_str(), parentName_.c_str(), module_.c_str(), name_.c_str() );
        NodeFactory::instance().getNodeDesc( parentModule_, parentName_ )->build( node );
        node->addSuper( parentName_ );
      }
      for ( FuncIterator func = funcs_.begin(); func != funcs_.end(); ++func )
      {
        node->addFunc( func->second->build( node ) );
      }
      for ( VarIterator var = vars_.begin(); var != vars_.end(); ++var )
      {
        node->addVar( var->second->build( node ) );
      }
      for ( NodeInterfaceIterator nodeInterface = nodeInterfaces_.begin(); nodeInterface != nodeInterfaces_.end(); ++nodeInterface )
      {
        std::string interfaceModule = nodeInterface->second.first;
        std::string interfaceName = nodeInterface->second.second;
        FHE_ASSERT_MSG( NodeFactory::instance().hasNodeInterface( interfaceModule, interfaceName ),
                        "unable to add unknown interface %s.%s to nodeDesc %s.%s",
                        interfaceModule.c_str(), interfaceName.c_str(), module_.c_str(), name_.c_str() );
        NodeFactory::instance().getNodeInterface( interfaceModule, interfaceName )->build( node );
      }
    }
  }
}
